home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
Common
/
ZMemUtils.cpp
< prev
next >
Wrap
Text File
|
1997-06-18
|
16KB
|
681 lines
/*
* File: ZMemUtils.cpp
* Summary: Misc memory utilities.
* Written by: Jesse Jones
*
* Copyright ゥ 1996-1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <3> 4/13/97 JDJ Includes Gestalt.h
* <2> 4/13/97 JDJ TemporaryZone no longer caches zone.
* <1> 1/14/96 JDJ Created.
*/
#include <ZMemUtils.h>
#include <Errors.h>
#include <Gestalt.h>
#include <Resources.h>
#include <ZDebug.h>
#include <ZExceptions.h>
#include <ZUnitTest.h>
// ===================================================================================
// Helper Functions
// ===================================================================================
//---------------------------------------------------------------
//
// LowestPossibleAddress
//
//---------------------------------------------------------------
static void* LowestPossibleAddress()
{
static void* address = &SystemZone()->heapData;
return address;
}
//---------------------------------------------------------------
//
// HighestPossibleAddress
//
//---------------------------------------------------------------
static void* HighestPossibleAddress()
{
static void* address = nil;
if (address == nil) {
long logicalRAM;
if (Gestalt(gestaltLogicalRAMSize, &logicalRAM) != noErr)
logicalRAM = 128*1024L*1024L;
address = (void*) logicalRAM;
}
return address;
}
#pragma mark -
// ===================================================================================
// Allocation
// ===================================================================================
//---------------------------------------------------------------
//
// CreatePtr
//
//---------------------------------------------------------------
void* CreatePtr(ulong bytes, bool zeroBytes)
{
void* ptr = nil;
ptr = NewPtr(bytes);
if (ptr != nil) {
if (zeroBytes)
SetMemory(ptr, 0, bytes);
#if DEBUG
else
SetMemory(ptr, kNewFill, bytes);
#endif
}
return ptr;
}
//---------------------------------------------------------------
//
// DisposeIfPtr
//
//---------------------------------------------------------------
void DisposeIfPtr(void* ptr)
{
if (ptr != nil) {
ValidatePtr(ptr);
#if DEBUG
long size = GetPtrSize((Ptr) ptr);
SetMemory(ptr, kFreeFill, (ulong) size);
#endif
DisposePtr((Ptr) ptr);
}
}
//---------------------------------------------------------------
//
// ValidatePtr
//
//---------------------------------------------------------------
#if DEBUG
void ValidatePtr(void* ptr)
{
if (ptr == nil)
DEBUGSTR("Ptr is nil.");
else if (((long) ptr & 0x3) != 0)
DEBUGSTR("Ptr is not on a long-word boundary (%lX).", (long) ptr);
else if (ptr < LowestPossibleAddress())
DEBUGSTR("Ptr is too small (%lX).", (long) ptr);
else if (ptr > HighestPossibleAddress())
DEBUGSTR("Ptr is too large (%lX).", (long) ptr);
}
#endif
//---------------------------------------------------------------
//
// CreateHandle
//
//---------------------------------------------------------------
Handle CreateHandle(ulong bytes, int options)
{
Handle hand = nil;
ASSERT(bytes < 16*1024L*1024L);
ASSERT((options & ~(kZeroBytes | kUseTempHeap | kUseAppHeap)) == 0);
bool zeroBytes = (options & kZeroBytes) == kZeroBytes;
bool tempMem = (options & kUseTempHeap) == kUseTempHeap;
bool appHeap = (options & kUseAppHeap) == kUseAppHeap;
if (tempMem) {
OSErr err;
hand = TempNewHandle((long) bytes, &err);
}
else
hand = NewHandle(bytes);
if (hand != nil) {
ValidateHandle(hand);
if (zeroBytes)
SetMemory(*hand, 0, bytes);
#if DEBUG
else
SetMemory(*hand, kNewFill, bytes);
#endif
}
return hand;
}
//---------------------------------------------------------------
//
// DisposeIfHandle
//
//---------------------------------------------------------------
void DisposeIfHandle(Handle hand)
{
if (hand != nil) {
ASSERT(hand != GZSaveHnd()); // ensure we're not messing with the growzoneユs protected handle
ASSERT(!IsResource(hand));
if (*hand != nil) {
ValidateHandle(hand);
#if DEBUG
ulong size = (ulong) GetHandleSize(hand);
SetMemory(*hand, kFreeFill, size);
#endif
} else
ASSERT(IsPurgeable(hand));
::DisposeHandle(hand);
}
}
//---------------------------------------------------------------
//
// ValidateHandle
//
//---------------------------------------------------------------
#if DEBUG
void ValidateHandle(Handle hand)
{
if (hand == nil)
DEBUGSTR("Handle is nil.");
else if (((long) hand & 0x3) != 0)
DEBUGSTR("Handle is not on a long-word boundary (%lX).", (long) hand);
else if (hand < LowestPossibleAddress())
DEBUGSTR("Handle is too small (%lX).", (long) hand);
else if (hand > HighestPossibleAddress())
DEBUGSTR("Handle is too large (%lX).", (long) hand);
else if (*hand == nil)
/* can be purged */;
else if (((long) *hand & 0x3) != 0)
DEBUGSTR("Master pointer is not on a long-word boundary (%lX).", (long) *hand);
else if (*hand < LowestPossibleAddress())
DEBUGSTR("Master pointer is too small (%lX).", (long) *hand);
else if (*hand > HighestPossibleAddress())
DEBUGSTR("Master pointer is too large (%lX).", (long) *hand);
#if 0 // Spotlight doesn't like this code
else {
THz zone = HandleZone(hand);
OSErr err = MemError();
if (err == noErr) {
if (zone == ApplicationZone()) { // Temp memory doesn't appear to be a real zone...
void* heapStart = &zone->heapData;
void* heapEnd = zone->bkLim;
if (hand < heapStart || hand >= heapEnd)
DEBUGSTR("Handle is outside of heap.");
else if (*hand < heapStart || *hand >= heapEnd)
DEBUGSTR("Master pointer is outside of heap.");
}
} else
DEBUGSTR("Error %d calling HandleZone.", err);
}
#endif
}
#endif
#pragma mark -
// ===================================================================================
// Handle State
// ===================================================================================
//---------------------------------------------------------------
//
// IsLocked
//
//---------------------------------------------------------------
bool IsLocked(Handle hand)
{
ValidateHandle(hand);
char state = HGetState(hand);
ASSERT(MemError() == noErr);
return (state & 0x80) != 0;
}
//---------------------------------------------------------------
//
// IsPurgeable
//
//---------------------------------------------------------------
bool IsPurgeable(Handle hand)
{
ASSERT(hand != nil);
char state = HGetState(hand);
OSErr err = MemError();
if (err == noErr)
return (state & 0x40) != 0;
else if (err == nilHandleErr)
return true; // Handle has been purged.
else
DEBUGSTR("Error %d in IsPurgeable.", err);
return false;
}
//---------------------------------------------------------------
//
// IsResource
//
//---------------------------------------------------------------
bool IsResource(Handle hand)
{
ValidateHandle(hand); // Might want to treat this like IsPurgeable.
char state = HGetState(hand);
ASSERT(MemError() == noErr);
return (state & 0x20) != 0;
}
#pragma mark -
// ===================================================================================
// Operations
// ===================================================================================
//---------------------------------------------------------------
//
// ClearMemory
//
//---------------------------------------------------------------
void ClearMemory(void* adr, ulong byteCount)
{
Byte* destPtr = (Byte *) adr;
Byte* endPtr = nil;
unsigned long longSetVal = 0;
long* longEndPtr = nil;
long* longSetValPtr;
endPtr = (Byte *) (destPtr + byteCount);
longEndPtr = (long *) ((long) (destPtr + byteCount) & 0xFFFFFFFC);// Trunc to nearest 4 bytes
// We do longword assignments when we have a chance
if (byteCount >= 4) {
while (((long) destPtr) & 0x00000003) // Starting on an odd byte boundry
*destPtr++ = 0;
// Assign in 4 byte chunks what we can
for (longSetValPtr = (long *) destPtr; longSetValPtr < longEndPtr;)
*longSetValPtr++ = 0;
destPtr = (Byte *) longSetValPtr;
}
// Now finish assigning odd bytes
while (destPtr < endPtr)
*destPtr++ = 0;
}
//---------------------------------------------------------------
//
// SetMemory
//
// Much thanks for this improved version to Scott D. Schmitz (from MacApp)
//
//---------------------------------------------------------------
void SetMemory(void* destPtr, Byte setVal, ulong byteCount)
{
// here are the results of using chars (1), longs (4) or doubles (8) on 68K and PowerPC
// 68K 601
//---------------------------------------
// char 261 (100) 237 (100)
// long 156 (60) 68 (29)
// double 933 (357) 46 (19)
//---------------------------------------
// the poor result for 68K/double is to be expected. Jeroen Schalk, DTS.
#if powerc
const unsigned long kBytesPerPunch = 8;
typedef double Punch, *PunchPtr; // select type double for best performance
#else
const unsigned long kBytesPerPunch = 4;
typedef unsigned long Punch, *PunchPtr; // select type long for best performance
#endif
Byte *begPtr = (Byte *) StripAddress(destPtr);
Byte *endPtr = (Byte *) (begPtr + byteCount);
// this looks like the right cutoff, since for the optimization
// O(n) = kBPP + kBPP/2 + n/kBPP + kBPP/2 = 2*kBPP + n/kBPP
// a simple linear fill has O(n) = n
// n > 2*kBPP + n/kBPP if n > 2*kBPP, since 1/kBPP ナ 0.
if (byteCount >= (4 * kBytesPerPunch)) {
// progress to nearest 4 byte boundary. O(n) = kBytesPerPunch/2
while ((long) begPtr & (kBytesPerPunch - 1))
*begPtr++ = setVal;
// lets get a kBytesPerPunch byte 'punch'. O(n) = kBytesPerPunch
register PunchPtr punchSetValPtr = (PunchPtr)begPtr; // save pointer to the first value
#if powerc
*begPtr++ = setVal; // fill it in
*begPtr++ = setVal;
*begPtr++ = setVal;
*begPtr++ = setVal;
#endif
*begPtr++ = setVal;
*begPtr++ = setVal;
*begPtr++ = setVal;
*begPtr++ = setVal;
register Punch punchSetVal = *punchSetValPtr++; // pick it up
// truncate to nearest kBytesPerPunch bytes
PunchPtr punchEndPtr = (PunchPtr)((long) endPtr & (0xFFFFFFFF - (kBytesPerPunch - 1)));
// assign in kBytesPerPunch byte chunks what we can. O(n) = n/kBytesPerPunch
while (punchSetValPtr < punchEndPtr)
*punchSetValPtr++ = punchSetVal;
begPtr = (Byte *) punchSetValPtr;
}
// now finish assigning odd bytes. O(n) = kBytesPerPunch/2 if optimization chosen.
while (begPtr < endPtr)
*begPtr++ = setVal;
}
//---------------------------------------------------------------
//
// InvertMemory
//
//---------------------------------------------------------------
void InvertMemory(void* adr, ulong byteCount)
{
ASSERT(adr != nil);
ASSERT(byteCount > 0);
Byte* ptr = (Byte *) adr;
if (((ulong) ptr & 0x1) || (byteCount & 0x3)) {
TRACEFLOW("ZWarning", "InvertBlock(): warning - using slow method (odd pointer or byte count not % 4)¥n");
Byte* bytes = (Byte *) ptr;
while (byteCount--)
*bytes++ ^= (Byte) (~0x0);
} else {
ulong* longs = (ulong *) ptr;
ulong longCount = byteCount >> 2;
while (longCount--)
*longs++ ^= ~0x0;
}
}
#pragma mark -
// ===================================================================================
// Bit twiddling
// ===================================================================================
//---------------------------------------------------------------
//
// TestBit
//
//---------------------------------------------------------------
bool TestBit(const void* bytePtr, ushort bit)
{
ushort index = (ushort) (bit >> 3);
ushort mask = (ushort) (1 << ((bit & 0x07) ^ 0x07));
return (((Byte *) bytePtr)[index] & mask) != 0;
}
//---------------------------------------------------------------
//
// SetBit
//
//---------------------------------------------------------------
void SetBit(void* bytePtr, ushort bit)
{
ushort index = (ushort) (bit >> 3);
Byte mask = (Byte) (1 << ((bit & 0x07) ^ 0x07));
((Byte *) bytePtr)[index] |= mask;
}
//---------------------------------------------------------------
//
// ClearBit
//
//---------------------------------------------------------------
void ClearBit(void* bytePtr, ushort bit)
{
ushort index = (ushort) (bit >> 3);
ushort mask = (Byte) (1 << ((bit & 0x07) ^ 0x07));
((Byte *) bytePtr)[index] &= (Byte) (~mask);
}
//---------------------------------------------------------------
//
// InvertBit
//
//---------------------------------------------------------------
void InvertBit(void* bytePtr, ushort bit)
{
ushort index = (ushort) (bit >> 3);
Byte mask = (Byte) (1 << ((bit & 0x07) ^ 0x07));
((Byte *) bytePtr)[index] ^= mask;
}
#pragma mark -
// ===================================================================================
// Heaps
// ===================================================================================
//---------------------------------------------------------------
//
// TemporaryZone
//
//---------------------------------------------------------------
THz TemporaryZone()
{
OSErr err;
Handle h = TempNewHandle(1, &err);
ThrowIfMemFail(h);
THz zone = HandleZone(h); // MMSystemFreeSpace (in MacApp) sez this can't be cached
DisposeHandle(h);
return zone;
}
//---------------------------------------------------------------
//
// WalkHeap
//
//---------------------------------------------------------------
#if DEBUG
void WalkHeap(THz zone, HeapWalkerProc callback, void* refCon)
{
ASSERT(zone != nil);
ASSERT(zone != TemporaryZone()); // temporary zone header is completely different than all the others...
ASSERT(callback != nil);
ASSERT((zone->heapType & kNewStyleHeap) != 0);
SHeapBlock* blockStart = reinterpret_cast<SHeapBlock*>((Byte*) zone + kZoneHeaderSize);
ASSERT(blockStart != nil);
SHeapBlock* block = reinterpret_cast<SHeapBlock*>((Byte*) zone + 64)->next;
ASSERT(block != nil);
while (block >= blockStart) { // last block points into zone header
(*callback)(block, refCon);
block = block->next;
ASSERT(block != nil);
}
}
#endif
#pragma mark -
// ===================================================================================
// Unit Test
// ===================================================================================
//---------------------------------------------------------------
//
// VerifyBlock
//
//---------------------------------------------------------------
#if DEBUG
static void VerifyBlock(Byte* block, ulong size, Byte value)
{
Byte* ptr = block;
for (long i = 0; i < size; i++)
ASSERT(*ptr++ == value);
}
#endif // DEBUG
//---------------------------------------------------------------
//
// TestMemUtils
//
//---------------------------------------------------------------
#if DEBUG
static void TestMemUtils()
{
Byte block1[100];
Byte block2[205];
ClearMemory(block1, 100);
VerifyBlock(block1, 100, 0);
SetMemory(block2, 205, 34);
VerifyBlock(block2, 34, 205);
InvertMemory(block1, 100);
VerifyBlock(block1, 100, 0xFF);
ASSERT(!EqualMemory(block1, block2, 100));
Handle h = CreateHandle(100, kUseAppHeap + kZeroBytes);
VerifyBlock((Byte*) *h, 100, 0);
ASSERT(!IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(!IsResource(h));
HLock(h);
ASSERT(IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(!IsResource(h));
HPurge(h);
ASSERT(IsLocked(h));
ASSERT(IsPurgeable(h));
ASSERT(!IsResource(h));
DisposeIfHandle(h);
h = CreateHandle(100, kUseTempHeap);
VerifyBlock((Byte*) *h, 100, kNewFill);
ASSERT(!IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(!IsResource(h));
HLock(h);
ASSERT(IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(!IsResource(h));
HPurge(h);
ASSERT(IsLocked(h));
ASSERT(IsPurgeable(h));
ASSERT(!IsResource(h));
DisposeIfHandle(h);
h = GetResource('SIZE', -1);
ASSERT(!IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(IsResource(h));
HLock(h);
ASSERT(IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(IsResource(h));
DetachResource(h);
ASSERT(IsLocked(h));
ASSERT(!IsPurgeable(h));
ASSERT(!IsResource(h));
HPurge(h);
ASSERT(IsLocked(h));
ASSERT(IsPurgeable(h));
ASSERT(!IsResource(h));
DisposeIfHandle(h);
TRACE("Completed memory utils test.¥n¥n");
}
static TUnitTestRegistrar sMemReg("Memory Utils", TestMemUtils);
#endif // DEBUG